using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Threading;
using System.Xml;
using System.Xml.XPath;
using Ricciolo.StylesExplorer.MarkupReflection;

namespace Ricciolo.StylesExplorer.Model
{
    class CompiledXamlFile : NotifyObject, IXamlFile, IXPathNavigable, IAsyncLoad
    {
        private IXamlData _xamlData;
        private BamlFile bamlFile;
        private readonly ITypeResolver resolver;
        private IDictionary<String, String> _namespaces;
        private bool _isLoading;
        private XamlDataCollection _resources;
        private IAssembly _assembly;

        public CompiledXamlFile(IAssembly assembly, BamlFile bamlFile, ITypeResolver resolver)
        {
            this._assembly = assembly;
            this.bamlFile = bamlFile;
            this.resolver = resolver;
        }

        #region IXamlFile Members

        /// <summary>
        /// Restituisce il nome del file XAML
        /// </summary>
        public string Name
        {
            get { return bamlFile.Uri.ToString(); }
        }

        /// <summary>
        /// Restituisce il riferimento all'assembly
        /// </summary>
        public IAssembly Assembly
        {
            get { return _assembly; }
        }

        /// <summary>
        /// Restituisce il codice XAML che rappresenta
        /// </summary>
        public IXamlData XamlData
        {
            get
            {
                this.EnsureLoaded();

                return _xamlData;
            }
            private set
            {
                _xamlData = value;
                this.OnPropertyChanged("XamlData");
            }
        }

        private void EnsureLoaded()
        {
            if (_xamlData == null && !this.IsLoading)
            {
                this.IsLoading = true;
                try
                {
                    _resources = new XamlDataCollection(this.Invoker);
                    LoadBaml(null);
                }
                finally
                {
                    this.IsLoading = false;
                }
            }
        }

        #region IAsyncLoad Members

        public bool IsLoading
        {
            get { return _isLoading; }
            private set
            {
                _isLoading = value;
                OnPropertyChanged("IsLoading");
            }
        }

        #endregion


        /// <summary>
        /// Restituisce la lista di risorse
        /// </summary>
        public IList<IXamlData> Resources
        {
            get
            {
                this.EnsureLoaded();

                return _resources;
            }
        }

        /// <summary>
        /// Restituisce la lista di namespaces
        /// </summary>
        public IDictionary<String, String> Namespaces
        {
            get
            {
                this.EnsureLoaded();

                return _namespaces;
            }
        }

        /// <summary>
        /// Istanzia il contenuto e lo restituisce
        /// </summary>
        public object LoadContent()
        {
            return this.bamlFile.LoadContent();
        }

        private void OnLoadBaml(object e)
        {
            if (this.Invoker.InvokeRequired)
            {
                this.Invoker.BeginInvoke(new WaitCallback(OnLoadBaml), new object[] { e });
                return;
            }

            if (e != null)
            {
                if (this.bamlFile.Stream.CanSeek)
                {
                    this.bamlFile.Stream.Position = 0;
                    // Load data
                    byte[] buffer = new byte[this.bamlFile.Stream.Length];
                    this.bamlFile.Stream.Read(buffer, 0, (int)this.bamlFile.Stream.Length);
                    throw new DecompilingXamlException("Error during baml load", buffer, (Exception)e);
                }
                else
                    throw new DecompilingXamlException("Error during baml load", (Exception)e);
            }
        }

        private void LoadBaml(object state)
        {
            try
            {
                try
                {
                    _resources.RaiseListChangedEvents = false;

                    // Mi assicuro che parta dall'inizio
                    if (this.bamlFile.Stream.CanSeek && this.bamlFile.Stream.Position > 0)
                    {
                        this.bamlFile.Stream.Seek(0, System.IO.SeekOrigin.Begin);
                    }

                    // Decompilo il baml
                    using (XmlBamlReader reader = new XmlBamlReader(this.bamlFile.Stream, resolver))
                    {
                        this.XamlData = new XamlData(this, new XPathDocument(reader));

                        XPathNavigator navigator = this._xamlData.CreateNavigator();

                        // Recupero la lista di risorse
                        XmlNamespaceManager nsManager = new XmlNamespaceManager(navigator.NameTable);
                        nsManager.AddNamespace("p", XmlPIMapping.PresentationNamespace);
                        nsManager.AddNamespace("x", XmlPIMapping.XamlNamespace);

                        XPathNodeIterator iterator = navigator.Select("/p:ResourceDictionary[1]/*", nsManager);

                        while (iterator.MoveNext())
                        {
                            Func<XamlData> fd = delegate { return new XamlData(this, iterator.Current.Clone()); };
                            XamlData xd = (XamlData)this.Invoker.Invoke(fd, null);
                            _resources.Add(xd);
                        }

                        _namespaces = reader.RootNamespaces;
                    }

                    OnLoadBaml(null);
                }
                finally
                {
                    OnPropertyChanged("Resources");
                    OnPropertyChanged("Namespaces");

                    _resources.RaiseListChangedEvents = true;
                    _resources.RaiseAdded();
                }
            }
            catch (ThreadAbortException te)
            { }
            catch (Exception e)
            {
                OnLoadBaml(e);
            }
        }

        #endregion

        #region IXPathNavigable Members

        ///<summary>
        ///Returns a new <see cref="T:System.Xml.XPath.XPathNavigator"></see> object. 
        ///</summary>
        ///
        ///<returns>
        ///An <see cref="T:System.Xml.XPath.XPathNavigator"></see> object.
        ///</returns>
        ///
        XPathNavigator IXPathNavigable.CreateNavigator()
        {
            if (this.XamlData != null)
                return this.XamlData.CreateNavigator();
            return null;
        }

        #endregion

    }
}
